package com.niiwoo.civet.user.service.local.portraitAuth;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.niiwoo.civet.trade.dto.response.OcrPortraitAuthConfigResponseDTO;
import com.niiwoo.civet.trade.service.configure.TradeConfigDubboService;
import com.niiwoo.civet.user.constants.LoginAccountErrorCode;
import com.niiwoo.civet.user.dao.entity.FacePlusPortrait;
import com.niiwoo.civet.user.dao.entity.FacePlusPortraitRecord;
import com.niiwoo.civet.user.dao.entity.IdentityInfo;
import com.niiwoo.civet.user.dao.entity.UserImageFile;
import com.niiwoo.civet.user.dao.mapper.FacePlusPortraitMapperExt;
import com.niiwoo.civet.user.dao.mapper.FacePlusPortraitRecordMapperExt;
import com.niiwoo.civet.user.dto.request.FaceIdVerifyReqDTO;
import com.niiwoo.civet.user.dto.response.FacePlusRespDTO;
import com.niiwoo.civet.user.enums.FacePlusAuditStatusEnum;
import com.niiwoo.civet.user.enums.PortraitChannelEnum;
import com.niiwoo.civet.user.enums.PortraitCompareTypeEnum;
import com.niiwoo.civet.user.enums.UserDataStatusEnum;
import com.niiwoo.civet.user.service.local.PortraitStatisticsService;
import com.niiwoo.civet.user.service.local.portraitAuth.dto.*;
import com.niiwoo.tripod.base.component.AsyncHttpClients;
import com.niiwoo.tripod.base.component.PrivacyMasks;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.tiancheng.component.TianchengAuthFaceIdService;
import com.niiwoo.tripod.tiancheng.enums.ComparisonTypeEnum;
import com.niiwoo.tripod.tiancheng.enums.FaceImageTypeEnum;
import com.niiwoo.tripod.tiancheng.exception.FaceAuthException;
import com.niiwoo.tripod.tiancheng.request.auth.faceid.FaceIdVerifyReq;
import com.niiwoo.tripod.tiancheng.response.auth.faceid.FaceIdVerifyResp;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.util.EntityUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class FacePlusPortraitAuth extends AbstractPortraitAuth {

    @Autowired
    FacePlusPortraitRecordMapperExt facePlusPortraitRecordMapperExt;

    @Autowired
    FacePlusPortraitMapperExt facePlusPortraitMapperExt;

    @Autowired
    AsyncHttpClients asyncHttpClients;

    @Autowired
    PrivacyMasks privacyMasks;

    @Autowired
    private TianchengAuthFaceIdService tianchengAuthFaceIdService;

    @Reference(version = "1.0.0")
    TradeConfigDubboService tradeConfigDubboService;

    @Autowired
    PortraitStatisticsService portraitStatisticsService;



    @Override
    protected boolean innerSaveAuthInfo(AuthInfoDTO authInfoDTO, UserImageFile frontImage, IdentityInfo identityInfo) {
        // 肖像认证记录
        FacePlusAuthInfoDTO facePlusAuthInfoDTO = (FacePlusAuthInfoDTO) authInfoDTO;
        FacePlusPortrait record = new FacePlusPortrait();
        record.setUserId(facePlusAuthInfoDTO.getUserId());
        record.setBestLiveImage(facePlusAuthInfoDTO.getLiveImageUrl());
        record.setDelta(facePlusAuthInfoDTO.getDelta());
        record.setLiveEnvImage(facePlusAuthInfoDTO.getFacePlusEnvImageUrl());
        record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.REVIEW.getValue());
        record.setIdImage(frontImage.getImgUrl());
        record.setIdImageAuthStatus(FacePlusAuditStatusEnum.REVIEW.getValue());
        return Optional.ofNullable(facePlusPortraitMapperExt.selectByPrimaryKey(record.getUserId()))
                        .map(old -> {
                            record.setLiveImageMaxConfigConfidence(0D);
                            record.setLiveImageMinConfigConfidence(0D);
                            record.setLiveImageConfidence(0D);
                            record.setLiveImageThreshold1e3(0D);
                            record.setLiveImageThreshold1e4(0D);
                            record.setLiveImageThreshold1e5(0D);
                            record.setLiveImageThreshold1e6(0D);
                            record.setSyntheticFaceConfidence(0D);
                            record.setSyntheticFaceThreshold(0D);
                            record.setMaskConfidence(0D);
                            record.setMaskThreshold(0D);
                            record.setScreenReplayConfidence(0D);
                            record.setScreenReplayThreshold(0D);
                            record.setIdImageConfidence(0D);
                            record.setLiveAuthErrorCode("");
                            record.setLiveAuthErrorMessage("");
                            record.setIdImageMaxConfigConfidence(0D);
                            record.setIdImageMinConfigConfidence(0D);
                            record.setIdImageThreshold1e3(0D);
                            record.setIdImageThreshold1e4(0D);
                            record.setIdImageThreshold1e5(0D);
                            record.setIdImageThreshold1e6(0D);
                            record.setIdImageQuality(0D);
                            record.setIdImageQualityThreshold(0D);
                            record.setIdCardAttacked((byte)0);
                            record.setIdAuthErrorCode("");
                            record.setIdAuthErrorMessage("");
                            return facePlusPortraitMapperExt.updateByPrimaryKeySelective(record) > 0;
                        })
                        .orElseGet(() -> {
                            record.setCreateTime(new Date());
                            return facePlusPortraitMapperExt.insertSelective(record) > 0;
                        });
    }

    public FacePlusRespDTO faceIdAuthVerify(FaceIdVerifyReqDTO reqDTO) {
        FacePlusRespDTO record = new FacePlusRespDTO();
        // FaceId++请求
        FaceIdVerifyReq faceIdVerifyReq = new FaceIdVerifyReq();
        faceIdVerifyReq.setIdcardName(reqDTO.getIdcardName());
        faceIdVerifyReq.setIdcardNumber(reqDTO.getIdcardNumber());
        faceIdVerifyReq.setComparisonType(ComparisonTypeEnum.SOURCE);
        faceIdVerifyReq.setFaceImageType(FaceImageTypeEnum.RAW_IMAGE);
        FaceIdVerifyReq.FaceIdVerifyOnePicInfo verifyOnePicInfo = new FaceIdVerifyReq.FaceIdVerifyOnePicInfo();
        verifyOnePicInfo.setFaceQualityThreshold("0");
        verifyOnePicInfo.setReturnFaces("1");
        verifyOnePicInfo.setFailWhenMultipleFaces("0");
        faceIdVerifyReq.setPicInfo(verifyOnePicInfo);

        CountDownLatch downloadLatch = new CountDownLatch(1);
        // 云端假脸攻击判定的照片
        this.downloadFile(reqDTO.getImageUrl(), "face++照片核对").whenComplete((r, t) -> {
            if (t != null) {
                log.error("face++对比###[" + reqDTO.getImageUrl() + "]照片下载异常", t);
                downloadLatch.countDown();
            } else {
                log.info("face++对比###手持身份证照片下载完成, size={}", r.length);
                verifyOnePicInfo.setImage(r);

                tianchengAuthFaceIdService.verify(faceIdVerifyReq).whenComplete((faceIdVerifyResp, throwable) -> {
                    try {
                        // 获取肖像认证配置
                        OcrPortraitAuthConfigResponseDTO configDTO = Optional.ofNullable(tradeConfigDubboService.queryOcrPortraitConfig()).orElseThrow(() -> new BizException(LoginAccountErrorCode.LOAD_OCR_CONFIG_FAIL));
                        // 配置的置信度区间
                        record.setMaxConfigConfidence(Double.valueOf(configDTO.getPortraitAuthFacePlusLiveImageThresholdMax()));
                        record.setMinConfigConfidence(Double.valueOf(configDTO.getPortraitAuthFacePlusLiveImageThresholdMin()));

                        // 置信度默认是0
                        record.setConfidence(0D);

                        if (throwable != null) {
                            if (throwable instanceof FaceAuthException) {
                                FaceAuthException faceAuthException = ((FaceAuthException) throwable);
                                String errorMessage = Optional.ofNullable(faceAuthException.getArgs()).filter(args -> args.length > 0).map(args -> (String) args[0]).orElse("");
                                log.info("face++肖像认证###身份证正面照###对比失败 code={}, message={}", faceAuthException.getErrorCode(), errorMessage);
                                record.setErrorCode(faceAuthException.getErrorCode());
                                record.setErrorMessage(errorMessage);
                                record.setAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
                            } else {
                                log.error(String.format("face++肖像认证###身份证正面照###对比异常, exceptionMessage=%s", throwable.getMessage()), throwable);
                                record.setErrorCode("unknown");
                                record.setErrorMessage("未知异常");
                                record.setAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
                            }
                        } else {
                            FaceIdVerifyResp.ResultFaceid resultFaceid = faceIdVerifyResp.getResultFaceid();

                            if(resultFaceid == null) {
                                record.setErrorMessage(faceIdVerifyResp.getErrorMessage());
                            } else {
                                FaceIdVerifyResp.Thresholds thresholds = resultFaceid.getThresholds();
                                // 置信度
                                record.setConfidence(Double.valueOf(resultFaceid.getConfidence()));

                                // “1e-3”：误识率为千分之一的置信度阈值；
                                record.setThreshold1e3(Double.valueOf(thresholds.getOne_e_3()));
                                // “1e-4”：误识率为万分之一的置信度阈值;
                                record.setThreshold1e4(Double.valueOf(thresholds.getOne_e_4()));
                                // “1e-5”：误识率为十万分之一的置信度阈值;
                                record.setThreshold1e5(Double.valueOf(thresholds.getOne_e_5()));
                                // “1e-6”：误识率为百万分之一的置信度阈值。
                                record.setThreshold1e6(Double.valueOf(thresholds.getOne_e_6()));
                                // 检测出的一张人脸图像的质量
//                        record.setQuality(Double.valueOf(faceIdVerifyResp.getFaces().get(0).getQuality()));
//                        record.setQualityThreshold(Double.valueOf(faceIdVerifyResp.getFaces().get(0).getQualityThreshold()));
                                // "id_attacked"：Int类型，判别身份证号码是否曾被冒用来攻击FaceID活体检测，取值1表示曾被攻击、取值0表示未被攻击。
//                        record.setIdCardAttacked(faceIdVerifyResp.getIdExceptions().getIdAttacked().byteValue());
                            }
                            // 大于最大置信度
                            if (record.getConfidence() >= record.getMaxConfigConfidence()) {
                                record.setAuthStatus(FacePlusAuditStatusEnum.SUCCESS.getValue());
                                // 大于于最小配置置信度
                            } else if (record.getConfidence() >= record.getMinConfigConfidence()) {
                                record.setAuthStatus(FacePlusAuditStatusEnum.NEED_AUDIT.getValue());
                            } else {
                                record.setAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
                            }
                        }
                    } finally {
                        downloadLatch.countDown();
                    }
                });
            }
        });

        if(! reqDTO.isAsync()) {
            try {
                downloadLatch.await(reqDTO.getTimeout(), TimeUnit.SECONDS);
            } catch (InterruptedException ignored) {
            }
        }
        return record;
    }

    @Override
    protected AuthCallInfoDTO innerCallAuth(String userId, IdentityInfo identityInfo) {
        FacePlusPortrait record = facePlusPortraitMapperExt.selectByUserIdAndStatus(userId, FacePlusAuditStatusEnum.REVIEW.getValue());
        if (record == null) {
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_REVIEW_RECORD_NOT_FOUND);
        }
        String idCard = privacyMasks.decryptPrivacy(identityInfo.getIdCard());
        // 活体照请求
        FaceIdVerifyReq liveImageVerifyReq = new FaceIdVerifyReq();
        liveImageVerifyReq.setIdcardName(identityInfo.getName());
        liveImageVerifyReq.setIdcardNumber(idCard);
        liveImageVerifyReq.setComparisonType(ComparisonTypeEnum.SOURCE);
        liveImageVerifyReq.setFaceImageType(FaceImageTypeEnum.MEGLIVE);
        FaceIdVerifyReq.FaceIdVerifyMegLiveSDKPicInfo liveImageInfo = new FaceIdVerifyReq.FaceIdVerifyMegLiveSDKPicInfo();
        liveImageVerifyReq.setPicInfo(liveImageInfo);
        // 身份证正面照请求
        FaceIdVerifyReq idImageVerifyReq = new FaceIdVerifyReq();
        idImageVerifyReq.setIdcardName(identityInfo.getName());
        idImageVerifyReq.setIdcardNumber(idCard);
        idImageVerifyReq.setComparisonType(ComparisonTypeEnum.SOURCE);
        idImageVerifyReq.setFaceImageType(FaceImageTypeEnum.RAW_IMAGE);
        FaceIdVerifyReq.FaceIdVerifyOnePicInfo idImageInfo = new FaceIdVerifyReq.FaceIdVerifyOnePicInfo();
        idImageInfo.setFaceQualityThreshold("0");
        idImageInfo.setReturnFaces("1");
        idImageVerifyReq.setPicInfo(idImageInfo);

        CountDownLatch downloadLatch = new CountDownLatch(4);

        // 云端假脸攻击判定的照片
        this.downloadFile(record.getLiveEnvImage(), "云端假脸攻击判定的照片").whenComplete((r, t) -> {
            try {
                if (t != null) {
                    log.error(String.format("face++对比###云端假脸攻击判定的照片下载异常, exception=%s", t.getMessage()));
                } else {
                    log.info("face++对比###云端假脸攻击判定的照片, size={}", r.length);
                    liveImageInfo.setImageEnv(r);
                }
            } finally {
                downloadLatch.countDown();
            }
        });
        // 最佳人脸照片
        this.downloadFile(record.getBestLiveImage(), "最佳人脸照片").whenComplete((r, t) -> {
            try {
                if (t != null) {
                    log.error(String.format("face++对比###最佳人脸照片, exception=%s", t.getMessage()));
                } else {
                    log.info("face++对比###最佳人脸照片, size={}", r.length);
                    liveImageInfo.setImageBest(r);
                }
            } finally {
                downloadLatch.countDown();
            }
        });
        // face++校验字符串
        this.downloadFile(record.getDelta(), "face++校验字符串").whenComplete((r, t) -> {
            try {
                if (t != null) {
                    log.error(String.format("face++对比###face++校验字符串, exception=%s", t.getMessage()));
                } else {
                    log.info("face++对比###face++校验字符串, size={}", r.length);
                    liveImageInfo.setDelta(new String(r, "UTF-8"));
                }
            } catch (Exception e) {
                log.error("face++对比###Delta编码错误", e);
            } finally {
                downloadLatch.countDown();
            }
        });
        // 身份证正面照
        this.downloadFile(record.getIdImage(), "身份证正面照").whenComplete((r, t) -> {
            try {
                if (t != null) {
                    log.error(String.format("face++对比###身份证正面照, exception=%s", t.getMessage()));
                } else {
                    log.info("face++对比###身份证正面照, size={}", r.length);
                    idImageInfo.setImage(r);
                }
            } finally {
                downloadLatch.countDown();
            }
        });
        // 肖像认证历史记录
        FacePlusPortrait updateRecord = new FacePlusPortrait();
        updateRecord.setUserId(userId);
        // 获取肖像认证配置
        OcrPortraitAuthConfigResponseDTO configDTO = Optional.ofNullable(tradeConfigDubboService.queryOcrPortraitConfig()).orElseThrow(() -> new BizException(LoginAccountErrorCode.LOAD_OCR_CONFIG_FAIL));
        // 等待图片下载完成
        try {
            downloadLatch.await();
        } catch (Exception e) {
            log.error("face++对比###下载图片线程执行异常", e);
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_FILE_DOWNLOAD_FAIL);
        }
        if (liveImageInfo.getDelta() == null || liveImageInfo.getImageBest() == null || liveImageInfo.getImageEnv() == null || idImageInfo.getImage() == null) {
            log.error("face++对比###下载图片失败, delta={}, liveImage={}, envImage={}, idImage={}", liveImageInfo.getDelta() == null, liveImageInfo.getImageBest() == null, liveImageInfo.getImageEnv() == null, idImageInfo.getImage() == null);
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_FILE_DOWNLOAD_FAIL);
        }
        // 发起肖像认证请求并等待完成, 完成后消息认证信息输出到 updateRecord 对象
        CountDownLatch reqCount = new CountDownLatch(2);
        // 活体照片认证
        tianchengAuthFaceIdService.verify(liveImageVerifyReq).whenComplete((faceIdVerifyResp, throwable) -> {
            try {
                FacePlusPortraitAuth.this.handleLiveImageAuthResponse(faceIdVerifyResp, throwable, updateRecord, configDTO);
            } catch (Exception e) {
                log.error(String.format("face++对比###活体照片对比异常, exception=%s", e.getMessage()), e);
            } finally {
                reqCount.countDown();
            }
        });
        // 身份证正面照认证
        tianchengAuthFaceIdService.verify(idImageVerifyReq).whenComplete((faceIdVerifyResp, throwable) -> {
            try {
                FacePlusPortraitAuth.this.handleIdImageAuthResponse(faceIdVerifyResp, throwable, updateRecord, configDTO);
            } catch (Exception e) {
                log.error(String.format("face++对比###身份证正面照对比异常, exception=%s", e.getMessage()), e);
            } finally {
                reqCount.countDown();
            }
        });
        // 等待请求完成
        try {
            reqCount.await();
        } catch (InterruptedException e) {
            log.error("face++对比###肖像认证请求线程执行异常", e);
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_REQUEST_ERROR);
        }
        log.info("face++对比###肖像认证结果, data={}", JSON.toJSONString(updateRecord));
        if (updateRecord.getIdImageAuthStatus() == null || updateRecord.getLiveImageAuthStatus() == null) {
            log.error("face++对比###肖像认证失败, IdImageAuthStatus={}, LiveImageAuthStatus={}", updateRecord.getIdImageAuthStatus(), updateRecord.getLiveImageAuthStatus());
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_REQUEST_ERROR);
        }
        FacePlusAuthCallInfoDTO callInfoDTO = new FacePlusAuthCallInfoDTO();
        callInfoDTO.setBestLiveImage(record.getBestLiveImage());
        callInfoDTO.setDelta(record.getDelta());
        callInfoDTO.setLiveEnvImage(record.getLiveEnvImage());
        callInfoDTO.setLiveImageMaxConfigConfidence(updateRecord.getLiveImageMaxConfigConfidence());
        callInfoDTO.setLiveImageMinConfigConfidence(updateRecord.getLiveImageMinConfigConfidence());
        callInfoDTO.setLiveImageConfidence(updateRecord.getLiveImageConfidence());
        callInfoDTO.setLiveImageThreshold1e3(updateRecord.getLiveImageThreshold1e3());
        callInfoDTO.setLiveImageThreshold1e4(updateRecord.getLiveImageThreshold1e4());
        callInfoDTO.setLiveImageThreshold1e5(updateRecord.getLiveImageThreshold1e5());
        callInfoDTO.setLiveImageThreshold1e6(updateRecord.getLiveImageThreshold1e6());
        callInfoDTO.setSyntheticFaceConfidence(updateRecord.getSyntheticFaceConfidence());
        callInfoDTO.setSyntheticFaceThreshold(updateRecord.getSyntheticFaceThreshold());
        callInfoDTO.setMaskConfidence(updateRecord.getMaskConfidence());
        callInfoDTO.setMaskThreshold(updateRecord.getMaskThreshold());
        callInfoDTO.setScreenReplayConfidence(updateRecord.getScreenReplayConfidence());
        callInfoDTO.setScreenReplayThreshold(updateRecord.getScreenReplayThreshold());
        callInfoDTO.setLiveImageAuthStatus(updateRecord.getLiveImageAuthStatus());
        callInfoDTO.setLiveAuthErrorCode(updateRecord.getLiveAuthErrorCode());
        callInfoDTO.setLiveAuthErrorMessage(updateRecord.getLiveAuthErrorMessage());
        callInfoDTO.setIdImage(record.getIdImage());
        callInfoDTO.setIdImageConfidence(updateRecord.getIdImageConfidence());
        callInfoDTO.setIdImageMaxConfigConfidence(updateRecord.getIdImageMaxConfigConfidence());
        callInfoDTO.setIdImageMinConfigConfidence(updateRecord.getIdImageMinConfigConfidence());
        callInfoDTO.setIdImageThreshold1e3(updateRecord.getIdImageThreshold1e3());
        callInfoDTO.setIdImageThreshold1e4(updateRecord.getIdImageThreshold1e4());
        callInfoDTO.setIdImageThreshold1e5(updateRecord.getIdImageThreshold1e5());
        callInfoDTO.setIdImageThreshold1e6(updateRecord.getIdImageThreshold1e6());
        callInfoDTO.setIdImageQuality(updateRecord.getIdImageQuality());
        callInfoDTO.setIdImageQualityThreshold(updateRecord.getIdImageQualityThreshold());
        callInfoDTO.setIdCardAttacked(updateRecord.getIdCardAttacked());
        callInfoDTO.setIdImageAuthStatus(updateRecord.getIdImageAuthStatus());
        callInfoDTO.setIdAuthErrorCode(updateRecord.getIdAuthErrorCode());
        callInfoDTO.setIdAuthErrorMessage(updateRecord.getIdAuthErrorMessage());
        // 最终认证状态
        callInfoDTO.setPortraitAuthStatus(this.getCombinedPortraitAduitStatus(updateRecord));
        log.error("face++对比###肖像认证结果2, data={}", JSON.toJSONString(callInfoDTO));
        // 肖像认证统计
        this.incrPortraitTimes(updateRecord.getLiveImageAuthStatus(), updateRecord.getLiveAuthErrorCode(), updateRecord.getLiveAuthErrorMessage(), PortraitCompareTypeEnum.LIVE_IMAGE_COMPARE);
        this.incrPortraitTimes(updateRecord.getIdImageAuthStatus(), updateRecord.getIdAuthErrorCode(), updateRecord.getIdAuthErrorMessage(), PortraitCompareTypeEnum.ID_CARD_FRONT_IMAGE_COMPARE);
        return callInfoDTO;
    }

    @Override
    protected UserDataStatusEnum.PortraitAuthStatus innerPostAuth(String userId, IdentityInfo identityInfo, AuthCallInfoDTO authCallInfoDTO) {
        FacePlusPortrait record = facePlusPortraitMapperExt.selectByUserIdAndStatus(userId, FacePlusAuditStatusEnum.REVIEW.getValue());
        if (record == null) {
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_REVIEW_RECORD_NOT_FOUND);
        }
        FacePlusAuthCallInfoDTO facePlusAuthCallInfoDTO = (FacePlusAuthCallInfoDTO) authCallInfoDTO;
        // 肖像认证记录
        FacePlusPortrait plusPortrait = new FacePlusPortrait();
        plusPortrait.setUserId(userId);
        plusPortrait.setLiveImageMaxConfigConfidence(facePlusAuthCallInfoDTO.getLiveImageMaxConfigConfidence());
        plusPortrait.setLiveImageMinConfigConfidence(facePlusAuthCallInfoDTO.getLiveImageMinConfigConfidence());
        plusPortrait.setLiveImageConfidence(facePlusAuthCallInfoDTO.getLiveImageConfidence());
        plusPortrait.setLiveImageThreshold1e3(facePlusAuthCallInfoDTO.getLiveImageThreshold1e3());
        plusPortrait.setLiveImageThreshold1e4(facePlusAuthCallInfoDTO.getLiveImageThreshold1e4());
        plusPortrait.setLiveImageThreshold1e5(facePlusAuthCallInfoDTO.getLiveImageThreshold1e5());
        plusPortrait.setLiveImageThreshold1e6(facePlusAuthCallInfoDTO.getLiveImageThreshold1e6());
        plusPortrait.setSyntheticFaceConfidence(facePlusAuthCallInfoDTO.getSyntheticFaceConfidence());
        plusPortrait.setSyntheticFaceThreshold(facePlusAuthCallInfoDTO.getSyntheticFaceThreshold());
        plusPortrait.setMaskConfidence(facePlusAuthCallInfoDTO.getMaskConfidence());
        plusPortrait.setMaskThreshold(facePlusAuthCallInfoDTO.getMaskThreshold());
        plusPortrait.setScreenReplayConfidence(facePlusAuthCallInfoDTO.getScreenReplayConfidence());
        plusPortrait.setScreenReplayThreshold(facePlusAuthCallInfoDTO.getScreenReplayThreshold());
        plusPortrait.setLiveImageAuthStatus(facePlusAuthCallInfoDTO.getLiveImageAuthStatus());
        plusPortrait.setLiveAuthErrorCode(facePlusAuthCallInfoDTO.getLiveAuthErrorCode());
        plusPortrait.setLiveAuthErrorMessage(facePlusAuthCallInfoDTO.getLiveAuthErrorMessage());
        plusPortrait.setIdImageConfidence(facePlusAuthCallInfoDTO.getIdImageConfidence());
        plusPortrait.setIdImageMaxConfigConfidence(facePlusAuthCallInfoDTO.getIdImageMaxConfigConfidence());
        plusPortrait.setIdImageMinConfigConfidence(facePlusAuthCallInfoDTO.getIdImageMinConfigConfidence());
        plusPortrait.setIdImageThreshold1e3(facePlusAuthCallInfoDTO.getIdImageThreshold1e3());
        plusPortrait.setIdImageThreshold1e4(facePlusAuthCallInfoDTO.getIdImageThreshold1e4());
        plusPortrait.setIdImageThreshold1e5(facePlusAuthCallInfoDTO.getIdImageThreshold1e5());
        plusPortrait.setIdImageThreshold1e6(facePlusAuthCallInfoDTO.getIdImageThreshold1e6());
        plusPortrait.setIdImageQuality(facePlusAuthCallInfoDTO.getIdImageQuality());
        plusPortrait.setIdImageQualityThreshold(facePlusAuthCallInfoDTO.getIdImageQualityThreshold());
        plusPortrait.setIdCardAttacked(facePlusAuthCallInfoDTO.getIdCardAttacked());
        plusPortrait.setIdImageAuthStatus(facePlusAuthCallInfoDTO.getIdImageAuthStatus());
        plusPortrait.setIdAuthErrorCode(facePlusAuthCallInfoDTO.getIdAuthErrorCode());
        plusPortrait.setIdAuthErrorMessage(facePlusAuthCallInfoDTO.getIdAuthErrorMessage());
        log.info("face++对比###记录更新###record={}", JSON.toJSONString(plusPortrait));
        facePlusPortraitMapperExt.updateByPrimaryKeySelective(plusPortrait);
        // 肖像认证历史
        FacePlusPortraitRecord updateFacePlusPortrait = new FacePlusPortraitRecord();
        updateFacePlusPortrait.setId(snowflakeIdWorker.nextId());
        updateFacePlusPortrait.setUserId(userId);
        updateFacePlusPortrait.setBestLiveImage(facePlusAuthCallInfoDTO.getBestLiveImage());
        updateFacePlusPortrait.setDelta(facePlusAuthCallInfoDTO.getDelta());
        updateFacePlusPortrait.setLiveEnvImage(facePlusAuthCallInfoDTO.getLiveEnvImage());
        updateFacePlusPortrait.setLiveImageMaxConfigConfidence(facePlusAuthCallInfoDTO.getLiveImageMaxConfigConfidence());
        updateFacePlusPortrait.setLiveImageMinConfigConfidence(facePlusAuthCallInfoDTO.getLiveImageMinConfigConfidence());
        updateFacePlusPortrait.setLiveImageConfidence(facePlusAuthCallInfoDTO.getLiveImageConfidence());
        updateFacePlusPortrait.setLiveImageThreshold1e3(facePlusAuthCallInfoDTO.getLiveImageThreshold1e3());
        updateFacePlusPortrait.setLiveImageThreshold1e4(facePlusAuthCallInfoDTO.getLiveImageThreshold1e4());
        updateFacePlusPortrait.setLiveImageThreshold1e5(facePlusAuthCallInfoDTO.getLiveImageThreshold1e5());
        updateFacePlusPortrait.setLiveImageThreshold1e6(facePlusAuthCallInfoDTO.getLiveImageThreshold1e6());
        updateFacePlusPortrait.setSyntheticFaceConfidence(facePlusAuthCallInfoDTO.getSyntheticFaceConfidence());
        updateFacePlusPortrait.setSyntheticFaceThreshold(facePlusAuthCallInfoDTO.getSyntheticFaceThreshold());
        updateFacePlusPortrait.setMaskConfidence(facePlusAuthCallInfoDTO.getMaskConfidence());
        updateFacePlusPortrait.setMaskThreshold(facePlusAuthCallInfoDTO.getMaskThreshold());
        updateFacePlusPortrait.setScreenReplayConfidence(facePlusAuthCallInfoDTO.getScreenReplayConfidence());
        updateFacePlusPortrait.setScreenReplayThreshold(facePlusAuthCallInfoDTO.getScreenReplayThreshold());
        updateFacePlusPortrait.setLiveImageAuthStatus(facePlusAuthCallInfoDTO.getLiveImageAuthStatus());
        updateFacePlusPortrait.setLiveAuthErrorCode(facePlusAuthCallInfoDTO.getLiveAuthErrorCode());
        updateFacePlusPortrait.setLiveAuthErrorMessage(facePlusAuthCallInfoDTO.getLiveAuthErrorMessage());
        updateFacePlusPortrait.setIdImage(facePlusAuthCallInfoDTO.getIdImage());
        updateFacePlusPortrait.setIdImageConfidence(facePlusAuthCallInfoDTO.getIdImageConfidence());
        updateFacePlusPortrait.setIdImageMaxConfigConfidence(facePlusAuthCallInfoDTO.getIdImageMaxConfigConfidence());
        updateFacePlusPortrait.setIdImageMinConfigConfidence(facePlusAuthCallInfoDTO.getIdImageMinConfigConfidence());
        updateFacePlusPortrait.setIdImageThreshold1e3(facePlusAuthCallInfoDTO.getIdImageThreshold1e3());
        updateFacePlusPortrait.setIdImageThreshold1e4(facePlusAuthCallInfoDTO.getIdImageThreshold1e4());
        updateFacePlusPortrait.setIdImageThreshold1e5(facePlusAuthCallInfoDTO.getIdImageThreshold1e5());
        updateFacePlusPortrait.setIdImageThreshold1e6(facePlusAuthCallInfoDTO.getIdImageThreshold1e6());
        updateFacePlusPortrait.setIdImageQuality(facePlusAuthCallInfoDTO.getIdImageQuality());
        updateFacePlusPortrait.setIdImageQualityThreshold(facePlusAuthCallInfoDTO.getIdImageQualityThreshold());
        updateFacePlusPortrait.setIdCardAttacked(facePlusAuthCallInfoDTO.getIdCardAttacked());
        updateFacePlusPortrait.setIdImageAuthStatus(facePlusAuthCallInfoDTO.getIdImageAuthStatus());
        updateFacePlusPortrait.setIdAuthErrorCode(facePlusAuthCallInfoDTO.getIdAuthErrorCode());
        updateFacePlusPortrait.setIdAuthErrorMessage(facePlusAuthCallInfoDTO.getIdAuthErrorMessage());
        updateFacePlusPortrait.setCreateTime(new Date());
        log.info("face++对比###历史记录更新###record={}", JSON.toJSONString(updateFacePlusPortrait));
        facePlusPortraitRecordMapperExt.insertSelective(updateFacePlusPortrait);
        return authCallInfoDTO.getPortraitAuthStatus();
    }

    @Override
    protected boolean innerInvalidateUnAuthInfo(String userId) {
        FacePlusPortrait record = facePlusPortraitMapperExt.selectByUserIdAndStatus(userId, FacePlusAuditStatusEnum.REVIEW.getValue());
        if (record == null) {
            throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_REVIEW_RECORD_NOT_FOUND);
        }
        // 将待审核的消息认证信息设置为丢弃
        FacePlusPortrait updateRecord = new FacePlusPortrait();
        updateRecord.setUserId(userId);
        updateRecord.setLiveImageAuthStatus(FacePlusAuditStatusEnum.DISCARDED.getValue());
        updateRecord.setIdImageAuthStatus(FacePlusAuditStatusEnum.DISCARDED.getValue());
        return facePlusPortraitMapperExt.updateByPrimaryKeySelective(updateRecord) > 0;
    }

    @Override
    protected <U extends AuthResultDTO> U innerGetAuthResult(String userId, UserDataStatusEnum.PortraitAuthStatus status, boolean isRecent, Class<U> cls) {
        FacePlusAuthResultDTO resultDTO = new FacePlusAuthResultDTO();
        if (!cls.isInstance(resultDTO)) {
            return null;
        }
        FacePlusPortrait record;
        if (isRecent) {
            record = ((FacePlusPortraitAuth) AopContext.currentProxy()).doWithTransaction(() -> facePlusPortraitMapperExt.selectByPrimaryKey(userId));
        } else {
            record = facePlusPortraitMapperExt.selectByPrimaryKey(userId);
        }
        if (record != null) {
            resultDTO.setBestLiveImage(record.getBestLiveImage());
            resultDTO.setDelta(record.getDelta());
            resultDTO.setLiveEnvImage(record.getLiveEnvImage());
            resultDTO.setLiveImageMaxConfigConfidence(record.getLiveImageMaxConfigConfidence());
            resultDTO.setLiveImageMinConfigConfidence(record.getLiveImageMinConfigConfidence());
            resultDTO.setLiveImageConfidence(record.getLiveImageConfidence());
            resultDTO.setLiveImageThreshold1e3(record.getLiveImageThreshold1e3());
            resultDTO.setLiveImageThreshold1e4(record.getLiveImageThreshold1e4());
            resultDTO.setLiveImageThreshold1e5(record.getLiveImageThreshold1e5());
            resultDTO.setLiveImageThreshold1e6(record.getLiveImageThreshold1e6());
            resultDTO.setSyntheticFaceConfidence(record.getSyntheticFaceConfidence());
            resultDTO.setSyntheticFaceThreshold(record.getSyntheticFaceThreshold());
            resultDTO.setMaskConfidence(record.getMaskConfidence());
            resultDTO.setMaskThreshold(record.getMaskThreshold());
            resultDTO.setScreenReplayConfidence(record.getScreenReplayConfidence());
            resultDTO.setScreenReplayThreshold(record.getScreenReplayThreshold());
            resultDTO.setLiveImageAuthStatus(record.getLiveImageAuthStatus());
            resultDTO.setLiveAuthErrorCode(record.getLiveAuthErrorCode());
            resultDTO.setLiveAuthErrorMessage(record.getLiveAuthErrorMessage());
            resultDTO.setIdImage(record.getIdImage());
            resultDTO.setIdImageConfidence(record.getIdImageConfidence());
            resultDTO.setIdImageMaxConfigConfidence(record.getIdImageMaxConfigConfidence());
            resultDTO.setIdImageMinConfigConfidence(record.getIdImageMinConfigConfidence());
            resultDTO.setIdImageThreshold1e3(record.getIdImageThreshold1e3());
            resultDTO.setIdImageThreshold1e4(record.getIdImageThreshold1e4());
            resultDTO.setIdImageThreshold1e5(record.getIdImageThreshold1e5());
            resultDTO.setIdImageThreshold1e6(record.getIdImageThreshold1e6());
            resultDTO.setIdImageQuality(record.getIdImageQuality());
            resultDTO.setIdImageQualityThreshold(record.getIdImageQualityThreshold());
            resultDTO.setIdCardAttacked(record.getIdCardAttacked());
            resultDTO.setIdImageAuthStatus(record.getIdImageAuthStatus());
            resultDTO.setIdAuthErrorCode(record.getIdAuthErrorCode());
            resultDTO.setIdAuthErrorMessage(record.getIdAuthErrorMessage());
        }
        resultDTO.setPortraitAuthStatus(status);
        resultDTO.setRemainingTimes(this.getRemainingTimes(userId));
        return cls.cast(resultDTO);
    }

    @Override
    public PortraitChannelEnum getSupportChannel() {
        return PortraitChannelEnum.FACE_PLUS;
    }

    /**
     * 肖像认证次数统计
     * @param status 肖像认证对比状态
     * @param errorCode 肖像认证错误码
     */
    private void incrPortraitTimes(byte status, String errorCode, String errorMessage, PortraitCompareTypeEnum compareType) {
        FacePlusAuditStatusEnum liveAuthStatus = FacePlusAuditStatusEnum.of(status);
        if (liveAuthStatus == null) {
            return;
        }
        switch (liveAuthStatus) {
            case SUCCESS:
            case FAILURE:
            case NEED_AUDIT: {
                portraitStatisticsService.incrSuccessTimes(PortraitChannelEnum.FACE_PLUS, compareType);
            } break;
            case SERVER_EXCEPTION: {
                if (this.isMatchFacePlusFailureErrorCode(errorCode)) {
                    portraitStatisticsService.incrFailTimes(PortraitChannelEnum.FACE_PLUS, compareType);
                } else {
                    portraitStatisticsService.incrExceptionTimes(PortraitChannelEnum.FACE_PLUS, compareType);
                }
            } break;
        }
    }

    /**
     * 是否命中face++失败错误码
     *
     * @param errorCode face++错误码
     */
    private boolean isMatchFacePlusFailureErrorCode(String errorCode) {
        return Arrays.asList("400", "403", "413", "414").contains(errorCode);
    }

    /**
     * 处理身份证正面照验证结果
     * @param faceIdVerifyResp
     * @param throwable
     * @param record
     */
    private void handleIdImageAuthResponse(FaceIdVerifyResp faceIdVerifyResp, Throwable throwable, FacePlusPortrait record, OcrPortraitAuthConfigResponseDTO configDTO) {
        if (throwable != null) {
            if (throwable instanceof FaceAuthException) {
                FaceAuthException faceAuthException = ((FaceAuthException) throwable);
                String errorMessage = Optional.ofNullable(faceAuthException.getArgs()).filter(args -> args.length > 0).map(args -> (String)args[0]).orElse("");
                log.info("face++肖像认证###身份证正面照###对比失败 code={}, message={}", faceAuthException.getErrorCode(), errorMessage);
                record.setIdAuthErrorCode(faceAuthException.getErrorCode());
                record.setIdAuthErrorMessage(errorMessage);
                record.setIdImageAuthStatus(FacePlusAuditStatusEnum.SERVER_EXCEPTION.getValue());
            } else {
                log.error(String.format("face++肖像认证###身份证正面照###对比异常, exceptionMessage=%s", throwable.getMessage()), throwable);
                record.setIdAuthErrorCode("unknown");
                record.setIdAuthErrorMessage("未知异常");
                record.setIdImageAuthStatus(FacePlusAuditStatusEnum.SERVER_EXCEPTION.getValue());
            }
        } else {
            FaceIdVerifyResp.ResultFaceid resultFaceid = faceIdVerifyResp.getResultFaceid();
            FaceIdVerifyResp.Thresholds thresholds = resultFaceid.getThresholds();
            // 置信度
            record.setIdImageConfidence(Double.valueOf(resultFaceid.getConfidence()));
            // 配置的置信度区间
            record.setIdImageMaxConfigConfidence(Double.valueOf(configDTO.getPortraitAuthFacePlusIdImageThresholdMax()));
            record.setIdImageMinConfigConfidence(Double.valueOf(configDTO.getPortraitAuthFacePlusIdImageThresholdMin()));
            // “1e-3”：误识率为千分之一的置信度阈值；
            record.setIdImageThreshold1e3(Double.valueOf(thresholds.getOne_e_3()));
            // “1e-4”：误识率为万分之一的置信度阈值;
            record.setIdImageThreshold1e4(Double.valueOf(thresholds.getOne_e_4()));
            // “1e-5”：误识率为十万分之一的置信度阈值;
            record.setIdImageThreshold1e5(Double.valueOf(thresholds.getOne_e_5()));
            // “1e-6”：误识率为百万分之一的置信度阈值。
            record.setIdImageThreshold1e6(Double.valueOf(thresholds.getOne_e_6()));
            // 检测出的一张人脸图像的质量
            record.setIdImageQuality(Double.valueOf(faceIdVerifyResp.getFaces().get(0).getQuality()));
            record.setIdImageQualityThreshold(Double.valueOf(faceIdVerifyResp.getFaces().get(0).getQualityThreshold()));
            // "id_attacked"：Int类型，判别身份证号码是否曾被冒用来攻击FaceID活体检测，取值1表示曾被攻击、取值0表示未被攻击。
            record.setIdCardAttacked(faceIdVerifyResp.getIdExceptions().getIdAttacked().byteValue());
            // 大于最大置信度
            if (record.getIdImageConfidence() >= record.getIdImageMaxConfigConfidence()) {
                record.setIdImageAuthStatus(FacePlusAuditStatusEnum.SUCCESS.getValue());
                // 大于于最小配置置信度
            } else if (record.getIdImageConfidence() >= record.getIdImageMinConfigConfidence()) {
                record.setIdImageAuthStatus(FacePlusAuditStatusEnum.NEED_AUDIT.getValue());
            } else {
                record.setIdImageAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
            }
        }
    }

    /**
     * 处理活体照片处理结果
     * @param faceIdVerifyResp
     * @param throwable
     * @param record
     */
    private void handleLiveImageAuthResponse(FaceIdVerifyResp faceIdVerifyResp, Throwable throwable, FacePlusPortrait record, OcrPortraitAuthConfigResponseDTO configDTO) {
        if (throwable != null) {
            if (throwable instanceof FaceAuthException) {
                FaceAuthException faceAuthException = ((FaceAuthException) throwable);
                String errorMessage = Optional.ofNullable(faceAuthException.getArgs()).filter(args -> args.length > 0).map(args -> (String)args[0]).orElse("");
                log.info("face++肖像认证###活体照片###对比失败 code={}, message={}", faceAuthException.getErrorCode(), errorMessage);
                record.setLiveAuthErrorCode(faceAuthException.getErrorCode());
                record.setLiveAuthErrorMessage(errorMessage);
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.SERVER_EXCEPTION.getValue());
            } else {
                log.error(String.format("face++肖像认证###活体照片###对比异常, exceptionMessage=%s", throwable.getMessage()), throwable);
                record.setLiveAuthErrorCode("unknown");
                record.setLiveAuthErrorMessage("未知异常");
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.SERVER_EXCEPTION.getValue());
            }
        } else {
            FaceIdVerifyResp.ResultFaceid resultFaceid = faceIdVerifyResp.getResultFaceid();
            FaceIdVerifyResp.Thresholds thresholds = resultFaceid.getThresholds();
            // 置信度
            record.setLiveImageConfidence(Double.valueOf(resultFaceid.getConfidence()));
            // “1e-3”：误识率为千分之一的置信度阈值；
            record.setLiveImageThreshold1e3(Double.valueOf(thresholds.getOne_e_3()));
            // “1e-4”：误识率为万分之一的置信度阈值;
            record.setLiveImageThreshold1e4(Double.valueOf(thresholds.getOne_e_4()));
            // “1e-5”：误识率为十万分之一的置信度阈值;
            record.setLiveImageThreshold1e5(Double.valueOf(thresholds.getOne_e_5()));
            // “1e-6”：误识率为百万分之一的置信度阈值。
            record.setLiveImageThreshold1e6(Double.valueOf(thresholds.getOne_e_6()));
            // 配置的置信度区间
            record.setLiveImageMaxConfigConfidence(Double.valueOf(configDTO.getPortraitAuthFacePlusLiveImageThresholdMax()));
            record.setLiveImageMinConfigConfidence(Double.valueOf(configDTO.getPortraitAuthFacePlusLiveImageThresholdMin()));
            //face++人脸照片为软件合成脸的置信度阈值
            Double syntheticFaceThreshold = Double.valueOf(configDTO.getPortraitAuthFacePlusSyntheticFaceThreshold());
            //face++人脸照片为面具的置信度阈值
            Double maskThreshold = Double.valueOf(configDTO.getPortraitAuthFacePlusMaskThreshold());
            //face++人脸照片为面具的置信度阈值
            Double screenReplayThreshold = Double.valueOf(configDTO.getPortraitAuthFacePlusScreenReplayThreshold());
            FaceIdVerifyResp.FaceGenuineness faceGenuineness = faceIdVerifyResp.getFaceGenuineness();
            // 软件合成脸的置信度
            record.setSyntheticFaceConfidence(Double.valueOf(faceGenuineness.getSyntheticFaceConfidence()));
            // record.setSyntheticFaceThreshold(Double.valueOf(faceGenuineness.getSyntheticFaceThreshold()));
            record.setSyntheticFaceThreshold(syntheticFaceThreshold);
            // 面具的置信度
            record.setMaskConfidence(Double.valueOf(faceGenuineness.getMaskConfidence()));
            // record.setMaskThreshold(Double.valueOf(faceGenuineness.getMaskThreshold()));
            record.setMaskThreshold(maskThreshold);
            // 屏幕翻拍的置信度
            record.setScreenReplayConfidence(Double.valueOf(faceGenuineness.getScreenReplayConfidence()));
            // record.setScreenReplayThreshold(Double.valueOf(faceGenuineness.getScreenReplayThreshold()));
            record.setScreenReplayThreshold(screenReplayThreshold);
            // synthetic_face_confidence < synthetic_face_threshold，可以认为人脸不是软件合成脸。
            if (record.getSyntheticFaceConfidence() >= syntheticFaceThreshold) {
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
                // mask_confidence < mask_threshold，可以认为人脸不是面具。
            } else if (record.getMaskConfidence() >= maskThreshold) {
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
                // screen_replay_confidence < screen_replay_threshold，可以认为人脸不是屏幕翻拍。
            } else if (record.getScreenReplayConfidence() >= screenReplayThreshold) {
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
                // 大于最大置信度
            } else if (record.getLiveImageConfidence() >= record.getLiveImageMaxConfigConfidence()) {
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.SUCCESS.getValue());
                // 大于于最小配置置信度
            } else if (record.getLiveImageConfidence() >= record.getLiveImageMinConfigConfidence()) {
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.NEED_AUDIT.getValue());
            } else {
                record.setLiveImageAuthStatus(FacePlusAuditStatusEnum.FAILURE.getValue());
            }
        }
    }

    /**
     * 根据两次图片对比结果，判断最终肖像认证状态
     */
    private UserDataStatusEnum.PortraitAuthStatus getCombinedPortraitAduitStatus(FacePlusPortrait record) {
        FacePlusAuditStatusEnum liveImageStatus = FacePlusAuditStatusEnum.of(record.getLiveImageAuthStatus());
        FacePlusAuditStatusEnum idImageStatus = FacePlusAuditStatusEnum.of(record.getIdImageAuthStatus());
        // 是否都成功
        boolean isBothSuccess = liveImageStatus == FacePlusAuditStatusEnum.SUCCESS && idImageStatus == FacePlusAuditStatusEnum.SUCCESS;
        if (isBothSuccess) {
            return UserDataStatusEnum.PortraitAuthStatus.PASS_CERTIFIED;
        }
        // 是否其中一个失败
        boolean isAnyFail = liveImageStatus == FacePlusAuditStatusEnum.FAILURE || idImageStatus == FacePlusAuditStatusEnum.FAILURE;
        if (isAnyFail) {
            return UserDataStatusEnum.PortraitAuthStatus.NOT_PASS;
        }
        return UserDataStatusEnum.PortraitAuthStatus.NEED_APPROVE;

    }

    /**
     * 文件下载
     * @param url 路径
     * @param logTag 日志标记
     * @return
     */
    private CompletableFuture<byte[]>   downloadFile(String url, String logTag) {
        CompletableFuture<byte[]> future = new CompletableFuture<>();
        asyncHttpClients.getRequest(url, new FutureCallback<HttpResponse>() {
            @Override
            public void completed(HttpResponse response) {
                try {
                    int code = response.getStatusLine().getStatusCode();
                    byte[] file = code == org.apache.http.HttpStatus.SC_OK ? EntityUtils.toByteArray(response.getEntity()) : null;
                    if (file == null || file.length == 0) {
                        log.error("肖像认证###下载图片 {} 失败, url={}", logTag, url);
                        throw new BizException(LoginAccountErrorCode.PORTRAIT_AUTH_FILE_DOWNLOAD_FAIL);
                    }
                    future.complete(file);
                } catch (Exception e) {
                    future.completeExceptionally(e);
                }
            }

            @Override
            public void failed(Exception e) {
                log.error("肖像认证###下载图片 {} 异常, url={}", logTag, url);
                future.completeExceptionally(e);
            }

            @Override
            public void cancelled() {
                log.error("肖像认证###下载图片 {} 被取消, url={}", logTag, url);
                future.cancel(true);
            }
        });
        return future;
    }




}
